
import java.util.ArrayList;
import java.util.List;

import objects.ValidPosition;


/**
 * Used in the Subset0Viewport to hold multiple Alignments as "one". The
 * requirements for a collapsedAlignment are as follows:
 * 
 * 1. Intron Retention - All alignment's start positions must be at most 1
 * position off from each other 2. Splice Sites - All alignment's splice site
 * must be the same.
 * 
 * @author stephenbwilliamson
 * 
 */

public class CollapsedAlignment extends Alignment {

	private List<Alignment> containedAlignments;
	private int referencePosition;
	private boolean spliced;

	public CollapsedAlignment(Alignment a) {
		super(a.getStartPosition(), a.getBounds().end);
		if (a.isSpliced())
			spliced = true;
		else
			spliced = false;
		containedAlignments = new ArrayList<Alignment>();
		referencePosition = a.getStartPosition();
		addAlignment(a);
	}

	@Override
	public ValidPosition getBounds() {
		int start = Integer.MAX_VALUE;
		int end = Integer.MIN_VALUE;

		for (Alignment a : containedAlignments) {
			ValidPosition b = a.getBounds();
			if (start > b.start)
				start = b.start;
			if (end < b.end)
				end = b.end;
		}

		return new ValidPosition(start, end);
	}

	public void setReferencePosition(int position) {
		referencePosition = position;
	}

	public boolean addAlignment(Alignment alignment) {
		if (isCandidateAddition(alignment))
			return false;

		containedAlignments.add(alignment);
		return true;
	}

	public int getAlignmentCount() {
		return containedAlignments.size();
	}

	public int getReferencePosition() {
		return referencePosition;
	}

	/**
	 * There are four ways to check if a candidate is an addition: - The
	 * collapsed Alignment is representing intron retention: 1. The candidate is
	 * an intron alignment (check for start position) 2. The candidate is a
	 * splice site alignment (not a good candidate) - The collapsed Alignment is
	 * representing a splice site alignment: 3. The candidate is an intron
	 * alignment (not a good candidate) 4. The candidate is a splice site
	 * alignment (check for splice start)
	 * 
	 * @param a
	 *            The alignment to check to see if it can be added
	 * @return Is the Alignment a good candidate for the collapsed set?
	 * 
	 *         TODO: Update to work with multiple splice sites.
	 */

	public boolean isCandidateAddition(Alignment a) {
		// We are working with different types of alignments...
		if (a.isSpliced() != spliced)
			return false;

		// Candidate Alignment is representing an intron retention set
		if (!spliced)
			return (Math.abs(referencePosition - a.getBounds().start) <= 1);

		// Candidate Alignment is representing a splice site set - a little bit
		// harder to calculate
		int spliceStart = a.getValidPositions().get(1).start;
		return (Math.abs(referencePosition - spliceStart) <= 1);
	}

	public static boolean isCandidateAddition(Alignment one, Alignment two) {
		// TODO: Check to make sure Candidates aren't CollapsedAlignments...
		if (one instanceof CollapsedAlignment
				&& two instanceof CollapsedAlignment)
			return false; // Two collapsed alignments aren't going to collapse
							// together..

		// Check to see if one or two is a Collapsed Alignment.
		if (one instanceof CollapsedAlignment)
			return ((CollapsedAlignment) one).isCandidateAddition(two);
		if (two instanceof CollapsedAlignment)
			return ((CollapsedAlignment) two).isCandidateAddition(one);

		// Candidates conflict - one is a splice site the other is an intron
		// retention
		if (one.isSpliced() != two.isSpliced())
			return false;

		if (one.isSpliced()) {
			// TODO: Only works with one splice site per alignment. Update to
			// more than one splice site
			int spliceStartOne = one.getValidPositions().get(1).start;
			int spliceStartTwo = two.getValidPositions().get(1).start;
			return (Math.abs(spliceStartTwo - spliceStartOne) <= 1);
		}
		// Both are intron retention
		return (Math.abs(one.getStartPosition() - two.getStartPosition()) <= 1);
	}

	@Override
	public boolean isSpliced() {
		return spliced;
	}
}
